package org.fastng.xml {
	
	import mx.collections.ArrayCollection;
	
	/**
	 * Parses FastNG XML given a URL or XML object.
	 */
	public class Parser {
		
		/**
		 * Parses FastNG XML from an XML object.
		 * 
		 * @param xml An instance of XML.
		 * 
		 * @return An instance of XmlSuite.
		 */
		public static function parseFromXML(xml:XML):XmlSuite {
			return parseSuite(xml);
		}
		
		/**
		 * Calls a URL to retrieve and parse FastNG XML.
		 * 
		 * @param path An instance of String.
		 * 
		 * @return An instance of XmlSuite.
		 */
		public static function parseFromURL(path:String):XmlSuite {
			
			//Returns an empty object, for now.
			return new XmlSuite();
		}
		
		private static function parseSuiteParameters(suite:XmlSuite, suiteNode:XML):void {
			
			//Loop through parameters nodes in suite node.
			for each (var paramNode:XML in suiteNode.parameter) {
				suite.addParam(paramNode.@name.toString(), paramNode.@value.toString());
			}
		}
		
		private static function parseTestParameters(test:XmlTest, testNode:XML):void {
			
			//Loop through parameters nodes in test node.
			for each (var paramNode:XML in testNode.parameter) {
				var param:XmlParameter = new XmlParameter(paramNode.@name.toString(), paramNode.@value.toString());
				test.parameters.addItem(param);
			}
		}
		
		private static function parseClasses(test:XmlTest, testNode:XML):void {
			
			//Loop through classes nodes in test node.
			for each (var classesNode:XML in testNode.classes) {
				
				//Loop through class nodes in classes node.
				for each (var classNode:XML in classesNode.children()) {
					var _class:XmlClass = new XmlClass();
					_class.name = classNode.@name.toString();
					parseMethods(_class, classNode);
					test.classes.addItem(_class);
				}
			}
		}
		
		private static function parseMethods(_class:XmlClass, classNode:XML):void {
			
			//Loop through methods nodes in class node.
			for each (var methodsNode:XML in classNode.methods) {
				
				//Loop through child nodes of methods node.
				for each (var methodNode:XML in methodsNode.children()) {
					var method:XmlMethod = new XmlMethod();
					method.name = methodNode.@name.toString();
					method.exclude = (methodNode.name() == "exclude");
					_class.methods.addItem(method);
				}
			}
		}
		
		private static function parsePackages(test:XmlTest, testNode:XML):void {
			
			//Loop through packages nodes in test node.
			for each (var packagesNode:XML in testNode.packages) {
				
				//Loop through package nodes in packages node.
				for each (var packageNode:XML in packagesNode.children()) {
					test.packages.addItem(packageNode.@name.toString());
				}
			}
		}
		
		private static function parseRunIncludesAndExcludes(test:XmlTest, runNode:XML):void {
			
			//Loop through child nodes of run node.
			for each (var singleRunNode:XML in runNode.children()) {
				if (singleRunNode.name() == "include") {
					test.runIncludes.addItem(singleRunNode.@name.toString());
				}
				else if (singleRunNode.name() == "exclude") {
					test.runExcludes.addItem(singleRunNode.@name.toString());
				}
				else {
					throw new Error("The node '" + singleRunNode.name() + "' " +
						"is not allowed in this context.");
				}
			}
		}
		
		private static function parseDefinitions(definition:XmlDefinition, defineNode:XML):void {
			
			//Loop through child nodes of define node.
			for each (var groupNode:XML in defineNode.children()) {
				var group:XmlGroup = new XmlGroup();
				group.name = groupNode.@name.toString();
				group.exclude = (groupNode.name() == "exclude");
				definition.addGroup(group);
			}
		}
		
		private static function parseGroups(test:XmlTest, testNode:XML):void {
			
			//Loop through groups nodes in test node.
			for each (var groupsNode:XML in testNode.groups) {
				
				//Loop through run nodes in groups node.
				for each (var runNode:XML in groupsNode.run) {
					parseRunIncludesAndExcludes(test, runNode);
				}
				
				//Loop through define nodes in groups node.
				for each (var defineNode:XML in groupsNode.define) {
					var definition:XmlDefinition = new XmlDefinition();
					definition.name = defineNode.@name.toString();
					parseDefinitions(definition, defineNode);
					test.definitions.addItem(definition);
				}
			}
		}
		
		private static function parseSuite(suiteNode:XML):XmlSuite {
			var suite:XmlSuite = new XmlSuite();
			suite.name = suiteNode.@name;
			parseSuiteParameters(suite, suiteNode);
			
			//Loop through test nodes in suite node.
			for each (var testNode:XML in suiteNode.test) {
				var test:XmlTest = new XmlTest();
				test.name = testNode.@name;
				parseTestParameters(test, testNode);
				parseGroups(test, testNode);
				parseClasses(test, testNode);
				parsePackages(test, testNode);
				suite.addTest(test);
			}
			return suite;
		}

	}
}